unit AI;


interface
uses AsphyreSprite, AsphyreDef, vectors2, Classes, PathFinder, Math, Paths
  ,Geometry;

type
TBrainStatus = (br_Walking, br_Attack, br_Static, br_WalkToAttack,
   br_Fleeing, br_Searching, br_Thinking, br_Rotating);

TAI = class
private
  FIQ:byte;
  FThinkSpeed:single;
  FSpriteEngine:TSpriteEngine;
  FNPC:TPhisicObject;
public
  property IQ:byte read FIQ write FIQ;
  property ThinkSpeed:single read FThinkSpeed write FThinkSpeed;
  procedure SetSpriteEngine(ASpriteEngine:TSpriteEngine);
  procedure SetNPC(ANPC:TPhisicObject);
  function Think:boolean;
  function AttackWayClear:boolean;
  function WayClear(ToPoint: TPoint2):boolean;
  constructor Create(ASpriteEngine:TSpriteEngine;ANPC:TPhisicObject;AIQ:byte;
    AThinkSpeed:single);
end;



implementation

uses Main, ImpactSprite, NPC_Storage, Weapon_Storage;

constructor TAI.Create(ASpriteEngine:TSpriteEngine;ANPC:TPhisicObject;AIQ:byte;
    AThinkSpeed:single);
begin
  inherited Create;
  SetSpriteEngine(ASpriteEngine);
  SetNPC(ANPC);
  FIQ:=AIQ;
  FThinkSpeed:=AThinkSpeed;
end;

function TAI.AttackWayClear:boolean;
var
i,m:integer;
p1,p2:TPoint2;
begin
  result:=true;
  with TNPC(FNPC) do
  for i:=0 to Engine.Count-1 do
  if not (Engine[i]=FNPC) then
  if not (Engine[i]=EnemySprite) then
  if not (Engine[i] is TBullet) then
  if Engine[i].DoCollision then
  begin
    if Engine[i].CollideMethod=cmRadius then
    m:=LineWithSphere(Point2(x,y),Point2(EnemySprite.x,EnemySprite.y),
       Point2(Engine[i].x,Engine[i].y),Engine[i].CollideRadius,p1,p2)
    else
    m:=LineWithQuad(Point2(x,y),Point2(EnemySprite.x,EnemySprite.y),
      Engine[i].CollideQuadrangle,p1,p2);
    if m>0 then
    begin
      result:=false;
      exit;
    end;
  end;
end;

function TAI.WayClear(ToPoint: TPoint2):boolean;
var
i,m:integer;
p1,p2:TPoint2;
begin
  result:=true;
  with TNPC(FNPC) do
  for i:=0 to Engine.Count-1 do
  if not (Engine[i]=FNPC) then
  if not (Engine[i]=EnemySprite) then
  if not (Engine[i] is TBullet) then
  if Engine[i].DoCollision then
  begin
    if Engine[i].CollideMethod=cmRadius then
    m:=LineWithSphere(Point2(x,y),ToPoint,
       Point2(Engine[i].x,Engine[i].y),Engine[i].CollideRadius,p1,p2)
    else
    m:=LineWithQuad(Point2(x,y),ToPoint,
      Engine[i].CollideQuadrangle,p1,p2);
    if m>0 then
    begin
      result:=false;
      exit;
    end;
  end;
end;
procedure TAI.SetSpriteEngine(ASpriteEngine:TSpriteEngine);
begin
  FSpriteEngine:=ASpriteEngine;
end;

procedure TAI.SetNPC(ANPC:TPhisicObject);
begin
  FNPC:=ANPC;
end;

function TAI.Think:boolean;
var
i,r:integer;
IsPoint:TPoint2;
p:TPath;
AttackRange:real;
AttackAngle:real;
PathClear,DangerPathClear,b:boolean;
begin
  r:=0;
  IsPoint:=Point2(9999,9999);

  with TNPC(FNPC) do
  begin
    b:=(BrainStatus=br_Attack)and Assigned(EnemySprite);
    if b then
    b:=(lengthline(x,y,EnemySprite.X,EnemySprite.y)<
      StaticConstants.SeeRadius);
{    if b then
    b:=(not EnemySprite.IsDead);}
  end;

  with TNPC(FNPC) do
  if b then
  IsPoint:=Point2(EnemySprite.x,EnemySprite.y)
  else
  for i := 0 to FSpriteEngine.Count-1 do
  if FSpriteEngine[i] Is TPhisicObject then
  if FSpriteEngine[i]<>FNPC then
  if Enemies.Find(FSpriteEngine[i].Name,r) then
  if not FSpriteEngine[i].IsDead then
  if not ((FSpriteEngine[i] is TPlayer)and(TPlayer(FSpriteEngine[i]).Killed)) then
  if (LengthLine(x,y,FSpriteEngine[i].X,FSpriteEngine[i].Y)<
    StaticConstants.SeeRadius) then
  begin
    if (IsPoint.x=9999)and(IsPoint.y=9999) then
    begin
      TNPC(FNPC).BrainStatus:=br_WalkToAttack;
      IsPoint:=Point2(FSpriteEngine[i].x,FSpriteEngine[i].y);
      EnemySprite:=FSpriteEngine[i];
    end
    else
    if LengthLine(IsPoint.x,IsPoint.y,x,y)>LengthLine(FSpriteEngine[i].x,
      FSpriteEngine[i].y,x,y) then
    begin
      IsPoint:=Point2(FSpriteEngine[i].x,FSpriteEngine[i].y);
      EnemySprite:=FSpriteEngine[i];
    end;
  end;

  if (IsPoint.x=9999)and(IsPoint.y=9999) then
  begin
    IsPoint:=Point2(random(1024)-random(1024),random(1024)-random(1024));
    TNPC(FNPC).BrainStatus:=br_Walking;
  end;

  with TNPC(FNPC) do
  begin
    if Health<MaxHealth/5 then
    BrainStatus:=br_Fleeing;
  end;

  PathClear:=WayClear(IsPoint);

  with TNPC(FNPC) do
  DangerPathClear:=PathSeeker.WayClear(Point(Min(Max(trunc((x+1024)/CellSize),0),MaxX),
      Min(Max(trunc((y+1024)/CellSize),0),MaxY)),
      Point(Min(Max(trunc((IsPoint.x+1024)/CellSize),0),MaxX),
      Min(Max(trunc((IsPoint.y+1024)/CellSize),0),MaxY)),true);

  with TNPC(FNPC) do
  if PathClear and (BrainStatus=br_Attack) then
  BrainStatus:=br_WalkToAttack;

  with TNPC(FNPC) do
  if not ((DangerPathClear or PathClear and (BrainStatus=
    br_Walking))or(PathClear and (BrainStatus=br_WalkToAttack))) then
  begin
    p:=PathSeeker.FindWay(Point(Min(Max(trunc((x+1024)/CellSize),0),MaxX),
      Min(Max(trunc((y+1024)/CellSize),0),MaxY)),
      Point(Min(Max(trunc((IsPoint.x+1024)/CellSize),0),MaxX),
      Min(Max(trunc((IsPoint.y+1024)/CellSize),0),MaxY)));
    if Length(p)>0 then
    begin
      IsPoint.x:=p[length(p)-1].X*CellSize-1024+CellSize/2;
      IsPoint.y:=p[length(p)-1].y*CellSize-1024+CellSize/2;
    end;
  end;

  with TNPC(FNPC) do
  begin
    if (BrainStatus=br_WalkToAttack)or(BrainStatus=br_Attack) then
    begin
      Mission:=IsPoint;
    end
    else
    if (BrainStatus=br_Fleeing) then
    begin
      Mission:=VecAdd2(Point2(x,y),VecSub2(Point2(x,y),
        Point2(IsPoint.X,IsPoint.Y)));
    end
    else
    if (BrainStatus=br_Walking) then
    begin
      if PtInCircle(Mission.x,Mission.y,X,y,CollideRadius) then
      Mission:=IsPoint;
    end
    else
    begin
      Mission:=Point2(random(1024)-random(1024),random(1024)-random(1024));
    end;
  end;

  TNPC(FNPC).WaitUntilRotate:=false;

  with TNPC(FNPC) do
  with StaticConstants do
  if (BrainStatus=br_WalkToAttack)or(BrainStatus=br_Attack) then
  if Assigned(EnemySprite) then
  for i := 0 to TNPC(FNPC).PartCount-1 do
  with TNPC(FNPC).Parts[i] do
  begin
    if PartWeapon.Melee then
    begin
      if (LengthLine(x,y,EnemySprite.X,EnemySprite.Y)<PartWeapon.MeleeRange) then
      begin
        WaitUntilRotate:=true;
        Stopping:=true;
        if InRange(round(Direction),
          round(GetAngle(x,y,EnemySprite.X,EnemySprite.Y)-32),
          round(GetAngle(x,y,EnemySprite.X,EnemySprite.Y)+32)) then
        begin
          BrainStatus:=br_Attack;
        end;
      end
      else
      BrainStatus:=br_WalkToAttack;
    end
    else
    begin
      if PartWeapon.Flying then
      AttackRange:=PartWeapon.MaxSpeed*255/PartWeapon.TickAcc
      else
      if PartWeapon.Mission then
      AttackRange:=PartWeapon.MissionMaxLength
      else
      AttackRange:=PartWeapon.MaxSpeed*50;
      if (LengthLine(x,y,EnemySprite.X,EnemySprite.Y)<=AttackRange) then
      begin
        if PathClear then
        begin
          WaitUntilRotate:=true;
        	Stopping:=true;
          if InRange(round(Direction),
            round(GetAngle(x,y,EnemySprite.X,EnemySprite.Y)-RandomCurrent/2),
            round(GetAngle(x,y,EnemySprite.X,EnemySprite.Y)+RandomCurrent/2)) then
          begin
             BrainStatus:=br_Attack;
          end;
        end;
      end
      else
      BrainStatus:=br_WalkToAttack;
    end;
  end;

  Result:=true;  
end;

end.
